Skip to content

Conversation

@thomhurst
Copy link

Implements Testcontainers support for Grafana with the following features:

  • Default Grafana image: grafana/grafana:11.0.0
  • Support for custom username/password authentication
  • Anonymous access configuration
  • Ability to mount datasources, dashboards, plugins, and notifiers
  • HTTP health check wait strategy
  • Connection string generation with embedded credentials

Implements Testcontainers support for Grafana with the following features:
- Default Grafana image: grafana/grafana:11.0.0
- Support for custom username/password authentication
- Anonymous access configuration
- Ability to mount datasources, dashboards, plugins, and notifiers
- HTTP health check wait strategy
- Connection string generation with embedded credentials

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@netlify
Copy link

netlify bot commented Aug 8, 2025

Deploy Preview for testcontainers-dotnet ready!

Name Link
🔨 Latest commit 6ed1670
🔍 Latest deploy log https://app.netlify.com/projects/testcontainers-dotnet/deploys/690e09951892a80008656261
😎 Deploy Preview https://deploy-preview-1509--testcontainers-dotnet.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@HofmeisterAn HofmeisterAn added module An official Testcontainers module enhancement New feature or request labels Sep 21, 2025
@HofmeisterAn HofmeisterAn force-pushed the develop branch 3 times, most recently from 4900ecd to 8fa5f1b Compare October 3, 2025 20:17
Copy link
Collaborator

@HofmeisterAn HofmeisterAn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! It looks great overall, but there are a few small things we need to fix before we can merge it.

Comment on lines +78 to +116
/// <summary>
/// Mounts a datasource configuration file.
/// </summary>
/// <param name="datasourceFilePath">The path to the datasource configuration file.</param>
/// <returns>A configured instance of <see cref="GrafanaBuilder" />.</returns>
public GrafanaBuilder WithDataSource(string datasourceFilePath)
{
return WithBindMount(datasourceFilePath, "/etc/grafana/provisioning/datasources/");
}

/// <summary>
/// Mounts a dashboard configuration file.
/// </summary>
/// <param name="dashboardFilePath">The path to the dashboard configuration file.</param>
/// <returns>A configured instance of <see cref="GrafanaBuilder" />.</returns>
public GrafanaBuilder WithDashboard(string dashboardFilePath)
{
return WithBindMount(dashboardFilePath, "/etc/grafana/provisioning/dashboards/");
}

/// <summary>
/// Mounts a plugin configuration file.
/// </summary>
/// <param name="pluginFilePath">The path to the plugin configuration file.</param>
/// <returns>A configured instance of <see cref="GrafanaBuilder" />.</returns>
public GrafanaBuilder WithPlugin(string pluginFilePath)
{
return WithBindMount(pluginFilePath, "/etc/grafana/provisioning/plugins/");
}

/// <summary>
/// Mounts a notifier configuration file.
/// </summary>
/// <param name="notifierFilePath">The path to the notifier configuration file.</param>
/// <returns>A configured instance of <see cref="GrafanaBuilder" />.</returns>
public GrafanaBuilder WithNotifier(string notifierFilePath)
{
return WithBindMount(notifierFilePath, "/etc/grafana/provisioning/notifiers/");
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mounting a share isn't aligned with our best practices. It can make the module incompatible with remote container runtimes. Instead, we recommend using the WithResourceMapping(FileInfo, FileInfo) API, which copies the files into the container.

.WithPortBinding(GrafanaPort, true)
.WithUsername(DefaultUsername)
.WithPassword(DefaultPassword)
.WithEnvironment("GF_AUTH_ANONYMOUS_ENABLED", "false")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.WithEnvironment("GF_AUTH_ANONYMOUS_ENABLED", "false")
.WithAnonymousAccessDisabled()

Comment on lines +78 to +116
/// <summary>
/// Mounts a datasource configuration file.
/// </summary>
/// <param name="datasourceFilePath">The path to the datasource configuration file.</param>
/// <returns>A configured instance of <see cref="GrafanaBuilder" />.</returns>
public GrafanaBuilder WithDataSource(string datasourceFilePath)
{
return WithBindMount(datasourceFilePath, "/etc/grafana/provisioning/datasources/");
}

/// <summary>
/// Mounts a dashboard configuration file.
/// </summary>
/// <param name="dashboardFilePath">The path to the dashboard configuration file.</param>
/// <returns>A configured instance of <see cref="GrafanaBuilder" />.</returns>
public GrafanaBuilder WithDashboard(string dashboardFilePath)
{
return WithBindMount(dashboardFilePath, "/etc/grafana/provisioning/dashboards/");
}

/// <summary>
/// Mounts a plugin configuration file.
/// </summary>
/// <param name="pluginFilePath">The path to the plugin configuration file.</param>
/// <returns>A configured instance of <see cref="GrafanaBuilder" />.</returns>
public GrafanaBuilder WithPlugin(string pluginFilePath)
{
return WithBindMount(pluginFilePath, "/etc/grafana/provisioning/plugins/");
}

/// <summary>
/// Mounts a notifier configuration file.
/// </summary>
/// <param name="notifierFilePath">The path to the notifier configuration file.</param>
/// <returns>A configured instance of <see cref="GrafanaBuilder" />.</returns>
public GrafanaBuilder WithNotifier(string notifierFilePath)
{
return WithBindMount(notifierFilePath, "/etc/grafana/provisioning/notifiers/");
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These builder APIs haven't been tested. We typically add builder APIs only for configurations that are essential to run an instance or required for specific use cases.

Comment on lines +32 to +42
public string GetConnectionString()
{
var endpoint = GetHttpEndpoint();
var username = _configuration.Username ?? GrafanaBuilder.DefaultUsername;
var password = _configuration.Password ?? GrafanaBuilder.DefaultPassword;
return new UriBuilder(endpoint)
{
UserName = username,
Password = password
}.ToString();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of this? The tests only use the HTTP header for authentication.

@coderabbitai
Copy link

coderabbitai bot commented Nov 7, 2025

Summary by CodeRabbit

  • New Features

    • Added Grafana module with container support including configurable credentials and anonymous access settings
    • Support for provisioning dashboards, data sources, plugins, and notifiers
    • HTTP endpoint access and connection string generation
  • Documentation

    • Added Grafana module documentation with version 11.0.0 reference
  • Tests

    • Added comprehensive test suite validating container lifecycle and HTTP interactions

Walkthrough

This PR introduces Grafana container support to Testcontainers by adding a new module with a builder for configuring containers, setting credentials, enabling anonymous access, and provisioning assets. The solution is extended with the new projects, documentation is updated, and comprehensive tests validate container lifecycle and HTTP interactions.

Changes

Cohort / File(s) Change Summary
Solution Integration
Testcontainers.sln, docs/modules/index.md
Added solution project entries for Testcontainers.Grafana and Testcontainers.Grafana.Tests with corresponding GUIDs; added Grafana module documentation with image grafana/grafana:11.0.0 and NuGet/source links.
Project Configuration
src/Testcontainers.Grafana/Testcontainers.Grafana.csproj, tests/Testcontainers.Grafana.Tests/Testcontainers.Grafana.Tests.csproj
Added project files targeting net8.0, net9.0, netstandard2.0, and netstandard2.1 for main project; test project targets net9.0 with xunit, coverlet, and test SDK dependencies.
Grafana Container Implementation
src/Testcontainers.Grafana/GrafanaBuilder.cs, src/Testcontainers.Grafana/GrafanaConfiguration.cs, src/Testcontainers.Grafana/GrafanaContainer.cs, src/Testcontainers.Grafana/Usings.cs
Added fluent builder pattern for Grafana configuration (username, password, anonymous access, provisioning); configuration class supporting copy and merge patterns; container class exposing HTTP endpoint and connection string methods.
Test Suite
tests/Testcontainers.Grafana.Tests/GrafanaContainerTest.cs, tests/Testcontainers.Grafana.Tests/Usings.cs, tests/Testcontainers.Grafana.Tests/.runs-on
Added comprehensive async lifecycle tests validating health endpoint, authenticated/unauthenticated access, connection string format; nested test suites for custom credentials and anonymous access modes; specified ubuntu-24.04 runner.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Builder as GrafanaBuilder
    participant Container as GrafanaContainer
    participant Docker as Docker Engine
    participant Grafana as Grafana Service

    User->>Builder: new GrafanaBuilder()
    Builder->>Builder: Init() - set defaults
    Note over Builder: image: 11.0.0<br/>port: 3000<br/>credentials: admin/admin
    
    User->>Builder: WithUsername("custom")
    User->>Builder: WithPassword("secret")
    User->>Builder: Build()
    
    Builder->>Container: create(GrafanaConfiguration)
    Container->>Docker: create container
    Docker->>Grafana: spin up
    
    rect rgb(200, 220, 240)
    Note over Docker,Grafana: Health Check Wait Strategy
    Grafana-->>Docker: health endpoint ready
    end
    
    User->>Container: GetHttpEndpoint()
    Container-->>User: http://localhost:3000
    
    User->>Container: GetConnectionString()
    Container-->>User: http://custom:secret@localhost:3000
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • GrafanaBuilder.cs: Verify builder pattern correctness, fluent API chaining, environment variable mapping for provisioning paths (datasources, dashboards, plugins, notifiers), and health check wait strategy configuration
  • GrafanaContainer.cs: Confirm connection string format includes all credentials and hostname, and endpoint construction matches mapped port retrieval
  • GrafanaConfiguration.cs: Validate constructor delegation patterns and merge logic for old/new value precedence
  • Test Coverage: Ensure test scenarios cover default and custom credentials, anonymous access toggle, and all HTTP interactions use correct endpoints

Suggested reviewers

  • hamidjahad

Poem

🐰 A Grafana tale, so bright and grand,
With dashboards rising from the land,
Through builders' hands, containers bloom,
Metrics dance in Docker's room,
New module hops into the test—
Testcontainers quest, simply blessed!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description omits mandatory template sections: 'What does this PR do?' and 'Why is it important?' are required but not provided in the expected format. Add 'What does this PR do?' and 'Why is it important?' sections to the description using the repository's template structure for clarity and consistency.
Docstring Coverage ⚠️ Warning Docstring coverage is 67.65% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: Add Grafana module' clearly and concisely summarizes the main change: addition of a new Grafana module to Testcontainers, matching the actual changeset which adds Grafana container support with builder, configuration, and test infrastructure.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
src/Testcontainers.Grafana/GrafanaContainer.cs (1)

32-42: Reconsider the need for this method given authentication patterns.

The GetConnectionString() method embeds credentials in the URI, but this approach has several concerns:

  1. Security: Embedding credentials in URIs is problematic as they may be logged, cached, or exposed in stack traces.
  2. Grafana authentication: Modern Grafana typically uses API keys or Bearer tokens in HTTP headers, not HTTP Basic Auth via URI.
  3. Unused pattern: As noted in the previous review, tests authenticate using HTTP headers, suggesting this method may not align with actual usage patterns.

If this method isn't required by consumers or tests, consider removing it. Otherwise, clarify its intended use case.

If the method must remain, consider documenting its purpose and warning about security implications:

     /// <summary>
     /// Gets the Grafana connection string.
     /// </summary>
+    /// <remarks>
+    /// This method embeds credentials in the URI for backward compatibility.
+    /// For production use, prefer API keys with the Authorization header.
+    /// Avoid logging or caching the returned connection string as it contains credentials.
+    /// </remarks>
     /// <returns>The Grafana connection string.</returns>
     public string GetConnectionString()

Alternatively, if tests and typical usage rely on HTTP header authentication, remove this method:

-    /// <summary>
-    /// Gets the Grafana connection string.
-    /// </summary>
-    /// <returns>The Grafana connection string.</returns>
-    public string GetConnectionString()
-    {
-        var endpoint = GetHttpEndpoint();
-        var username = _configuration.Username ?? GrafanaBuilder.DefaultUsername;
-        var password = _configuration.Password ?? GrafanaBuilder.DefaultPassword;
-        return new UriBuilder(endpoint)
-        {
-            UserName = username,
-            Password = password
-        }.ToString();
-    }
src/Testcontainers.Grafana/GrafanaBuilder.cs (2)

83-116: Replace WithBindMount with WithResourceMapping for better compatibility.

As noted in previous reviews, mounting shares with WithBindMount can cause incompatibility with remote container runtimes. The recommended approach is to use WithResourceMapping(FileInfo, FileInfo) to copy files into the container.

Additionally, these provisioning methods lack test coverage. Consider adding tests or documenting the expected usage patterns.

Based on past review feedback.


133-133: Use WithAnonymousAccessDisabled() for consistency.

As noted in previous reviews, line 133 should use the WithAnonymousAccessDisabled() method instead of directly setting the environment variable.

Apply this diff:

             .WithUsername(DefaultUsername)
             .WithPassword(DefaultPassword)
-            .WithEnvironment("GF_AUTH_ANONYMOUS_ENABLED", "false")
+            .WithAnonymousAccessDisabled()
             .WithEnvironment("GF_AUTH_BASIC_ENABLED", "true")

Based on past review feedback.

🧹 Nitpick comments (6)
tests/Testcontainers.Grafana.Tests/Testcontainers.Grafana.Tests.csproj (1)

3-3: Consider multi-targeting for broader test coverage.

The test project targets only net9.0, whereas the main Grafana project supports net8.0;net9.0;netstandard2.0;netstandard2.1. While this may be intentional for simplicity, testing against multiple frameworks ensures compatibility across the supported target frameworks.

If broader coverage is desired, apply this diff:

-    <TargetFrameworks>net9.0</TargetFrameworks>
+    <TargetFrameworks>net8.0;net9.0</TargetFrameworks>
tests/Testcontainers.Grafana.Tests/GrafanaContainerTest.cs (5)

27-28: Consider removing ConfigureAwait(true).

In test code, ConfigureAwait(true) is redundant since that's the default behavior. Omitting it simplifies the code without changing functionality.

Apply this diff to simplify:

-        using var httpResponse = await httpClient.GetAsync("api/health", TestContext.Current.CancellationToken)
-            .ConfigureAwait(true);
+        using var httpResponse = await httpClient.GetAsync("api/health", TestContext.Current.CancellationToken);

46-47: Consider removing ConfigureAwait(true).

Same as the previous test, ConfigureAwait(true) is redundant in test code.

Apply this diff:

-        using var httpResponse = await httpClient.GetAsync("api/org", TestContext.Current.CancellationToken)
-            .ConfigureAwait(true);
+        using var httpResponse = await httpClient.GetAsync("api/org", TestContext.Current.CancellationToken);

53-65: Consider validating the URI format.

While the current assertions verify that key components are present in the connection string, they don't validate that the connection string is a well-formed URI with credentials properly encoded.

Consider adding a URI parsing assertion:

     public void GetConnectionStringReturnsExpectedFormat()
     {
         // When
         var connectionString = _grafanaContainer.GetConnectionString();

         // Then
         Assert.NotNull(connectionString);
+        Assert.True(Uri.TryCreate(connectionString, UriKind.Absolute, out var uri), "Connection string should be a valid URI");
         Assert.Contains(GrafanaBuilder.DefaultUsername, connectionString);
         Assert.Contains(GrafanaBuilder.DefaultPassword, connectionString);
         Assert.Contains(_grafanaContainer.Hostname, connectionString);
     }

97-98: Consider removing ConfigureAwait(true).

Consistent with previous suggestions, ConfigureAwait(true) is redundant in test code.

Apply this diff:

-            using var httpResponse = await httpClient.GetAsync("api/org", TestContext.Current.CancellationToken)
-                .ConfigureAwait(true);
+            using var httpResponse = await httpClient.GetAsync("api/org", TestContext.Current.CancellationToken);

131-132: Consider removing ConfigureAwait(true).

Consistent with previous suggestions, ConfigureAwait(true) is redundant in test code.

Apply this diff:

-            using var httpResponse = await httpClient.GetAsync("api/org", TestContext.Current.CancellationToken)
-                .ConfigureAwait(true);
+            using var httpResponse = await httpClient.GetAsync("api/org", TestContext.Current.CancellationToken);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c56984f and 6ed1670.

📒 Files selected for processing (11)
  • Testcontainers.sln (6 hunks)
  • docs/modules/index.md (1 hunks)
  • src/Testcontainers.Grafana/GrafanaBuilder.cs (1 hunks)
  • src/Testcontainers.Grafana/GrafanaConfiguration.cs (1 hunks)
  • src/Testcontainers.Grafana/GrafanaContainer.cs (1 hunks)
  • src/Testcontainers.Grafana/Testcontainers.Grafana.csproj (1 hunks)
  • src/Testcontainers.Grafana/Usings.cs (1 hunks)
  • tests/Testcontainers.Grafana.Tests/.runs-on (1 hunks)
  • tests/Testcontainers.Grafana.Tests/GrafanaContainerTest.cs (1 hunks)
  • tests/Testcontainers.Grafana.Tests/Testcontainers.Grafana.Tests.csproj (1 hunks)
  • tests/Testcontainers.Grafana.Tests/Usings.cs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
tests/Testcontainers.Grafana.Tests/GrafanaContainerTest.cs (2)
src/Testcontainers.Grafana/GrafanaBuilder.cs (15)
  • GrafanaContainer (119-123)
  • GrafanaBuilder (18-22)
  • GrafanaBuilder (28-32)
  • GrafanaBuilder (42-46)
  • GrafanaBuilder (53-57)
  • GrafanaBuilder (63-66)
  • GrafanaBuilder (72-76)
  • GrafanaBuilder (83-86)
  • GrafanaBuilder (93-96)
  • GrafanaBuilder (103-106)
  • GrafanaBuilder (113-116)
  • GrafanaBuilder (126-140)
  • GrafanaBuilder (143-146)
  • GrafanaBuilder (149-152)
  • GrafanaBuilder (155-158)
src/Testcontainers.Grafana/GrafanaContainer.cs (3)
  • GrafanaContainer (13-17)
  • GetHttpEndpoint (23-26)
  • GetConnectionString (32-42)
src/Testcontainers.Grafana/GrafanaBuilder.cs (2)
src/Testcontainers.Grafana/GrafanaConfiguration.cs (6)
  • PublicAPI (4-71)
  • GrafanaConfiguration (12-18)
  • GrafanaConfiguration (24-28)
  • GrafanaConfiguration (34-38)
  • GrafanaConfiguration (44-48)
  • GrafanaConfiguration (55-60)
src/Testcontainers.Grafana/GrafanaContainer.cs (2)
  • PublicAPI (4-43)
  • GrafanaContainer (13-17)
src/Testcontainers.Grafana/GrafanaConfiguration.cs (2)
src/Testcontainers.Grafana/GrafanaBuilder.cs (1)
  • PublicAPI (4-159)
src/Testcontainers.Grafana/GrafanaContainer.cs (1)
  • PublicAPI (4-43)
src/Testcontainers.Grafana/GrafanaContainer.cs (2)
src/Testcontainers.Grafana/GrafanaBuilder.cs (16)
  • PublicAPI (4-159)
  • GrafanaContainer (119-123)
  • GrafanaBuilder (18-22)
  • GrafanaBuilder (28-32)
  • GrafanaBuilder (42-46)
  • GrafanaBuilder (53-57)
  • GrafanaBuilder (63-66)
  • GrafanaBuilder (72-76)
  • GrafanaBuilder (83-86)
  • GrafanaBuilder (93-96)
  • GrafanaBuilder (103-106)
  • GrafanaBuilder (113-116)
  • GrafanaBuilder (126-140)
  • GrafanaBuilder (143-146)
  • GrafanaBuilder (149-152)
  • GrafanaBuilder (155-158)
src/Testcontainers.Grafana/GrafanaConfiguration.cs (6)
  • PublicAPI (4-71)
  • GrafanaConfiguration (12-18)
  • GrafanaConfiguration (24-28)
  • GrafanaConfiguration (34-38)
  • GrafanaConfiguration (44-48)
  • GrafanaConfiguration (55-60)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: ci (Testcontainers, ubuntu-24.04)
  • GitHub Check: analyze (csharp)
🔇 Additional comments (21)
tests/Testcontainers.Grafana.Tests/.runs-on (1)

1-1: LGTM!

The runner specification is appropriate for Docker-based Grafana container tests.

src/Testcontainers.Grafana/Usings.cs (1)

1-8: LGTM!

The global usings are appropriate for the Grafana module and follow the standard pattern used across other Testcontainers modules.

tests/Testcontainers.Grafana.Tests/Usings.cs (1)

1-6: LGTM!

The global usings are appropriate for the Grafana test project.

src/Testcontainers.Grafana/Testcontainers.Grafana.csproj (1)

1-12: LGTM!

The project configuration follows the standard pattern for Testcontainers modules with appropriate multi-targeting and dependencies.

docs/modules/index.md (1)

45-45: LGTM!

The Grafana module entry is correctly formatted and positioned alphabetically in the modules table.

Testcontainers.sln (1)

65-66: LGTM!

The solution file correctly adds both Grafana projects with proper build configurations and folder nesting, following the established solution structure.

Also applies to: 185-186, 365-368, 605-608, 812-812, 872-872

src/Testcontainers.Grafana/GrafanaContainer.cs (2)

9-17: LGTM!

The constructor follows the standard pattern for Testcontainers modules.


19-26: LGTM!

The GetHttpEndpoint() method correctly constructs the Grafana HTTP endpoint using the mapped port.

tests/Testcontainers.Grafana.Tests/GrafanaContainerTest.cs (3)

3-16: LGTM!

The test class setup correctly implements IAsyncLifetime for proper container lifecycle management. The container is appropriately shared across tests in this class.


67-83: LGTM!

The nested test class properly isolates custom credential testing with its own container instance. The IAsyncLifetime implementation ensures proper resource cleanup.


105-120: LGTM!

The nested test class properly validates anonymous access configuration with its own isolated container instance.

src/Testcontainers.Grafana/GrafanaConfiguration.cs (4)

12-18: LGTM!

The basic constructor correctly initializes the configuration with optional username and password parameters.


55-60: LGTM!

The merge constructor correctly implements the immutable copy pattern, preferring new values when present and falling back to old values when null.


65-70: LGTM!

The get-only properties enforce immutability as expected.


24-38: ****

The review comment incorrectly identifies a credential loss issue. The code properly preserves Username and Password during cloning.

When Clone() is called, it creates a new GrafanaConfiguration via the copy constructors (lines 24-28 or 34-38), which intentionally have null properties. This is then passed to the Merge method, which invokes the merge constructor (lines 54-62). The merge constructor explicitly preserves credentials from the old configuration using null-coalescing operators:

Username = newValue.Username ?? oldValue.Username;
Password = newValue.Password ?? oldValue.Password;

Since DockerResourceConfiguration contains the previously configured credentials and serves as oldValue in the merge operation, credentials are retained when cloning. This pattern is consistent across all container modules in the codebase, confirming it is intentional framework design.

Likely an incorrect or invalid review comment.

src/Testcontainers.Grafana/GrafanaBuilder.cs (6)

18-32: LGTM!

The constructors correctly implement the builder pattern with proper initialization.


42-57: LGTM!

The credential configuration methods correctly implement the fluent, immutable builder pattern and set appropriate Grafana environment variables.


72-76: LGTM! Note the admin role for anonymous access.

The anonymous access methods are correctly implemented. Note that enabling anonymous access grants Admin role to anonymous users, which is appropriate for testing scenarios but should be documented for production use.


119-123: LGTM!

The Build method correctly validates configuration before constructing the container.


143-158: LGTM!

The Clone and Merge methods correctly implement the immutable builder pattern. Note that the Clone methods may not preserve Username/Password properties, which was flagged in the GrafanaConfiguration.cs review.


7-7: ---

Update Grafana image to 12.3.0 or confirm 11.0.0 is required.

The image is pinned to version 11.0.0, but the latest stable version is 12.3.0. If there's no specific reason to remain on 11.0.0 (e.g., testing backward compatibility), consider updating to the current stable release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request module An official Testcontainers module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants